﻿/*----------------------------------------------------------------
// 公司名称：公司名称
//
// 产品名称：Gaf
//
// 创 建 人：netcasewqs
// 创建日期：2022/3/4
// 邮    箱：netcasewqs@qq.com
//
// Copyright (C) 2022 Gaf，保留所有权利。
//
//----------------------------------------------------------------*/

using System.Runtime.InteropServices;

namespace Gaf.Impl;

internal sealed partial class GafImpl : IGaf, IDisposable
{
    private SystemAddinActivator _systemAddinActivator = null!;
    internal AddinRepository _repository;

    internal readonly Dictionary<string, string> _managedLibsPath = new();
    internal readonly Dictionary<string, string> _unManagedLibsPath = new();

    private readonly AssemblyLoadContext _assemblyLoader = AssemblyLoadContext.Default;

    private readonly HashSet<IntPtr> _unmanagedLibs = new();
    private LogListener? _logListener; 

    public GafImpl(GafOptions frameworkDescriptor)
    {
        Options = frameworkDescriptor;
        if (Log.Enabled) _logListener = new LogListener(this);

        _repository = new AddinRepository();
        AddinManager = new AddinManager(this);
        IdGenerator.Instance.Reset();

        PopulateManagedLibsPath(Options.ManagedLibsDirectory, true);
        PopulateManagedLibsPath(Options.UnmanagedLibsDirectory, false);

        _assemblyLoader.Resolving += OnAssemblyResolving;

    }

    private IntPtr OnResovingUnmanagedDll(Assembly asm, string libraryName)
    {
        try
        {
            if (_unManagedLibsPath.TryGetValue(libraryName!, out var path))
            {
                //var result = NativeLibrary.Load(libraryName,asm,DllImportSearchPath.UseDllDirectoryForDependencies);
                //return result;
                var ptr = NativeLibrary.Load(path);
                _unmanagedLibs.Add(ptr);

                return ptr;
            }

            return default;
        }
        catch
        {
            return default;
        }
    }

    private Assembly? OnAssemblyResolving(AssemblyLoadContext ctx, AssemblyName assemblyName)
    {
        try
        {
            if (_managedLibsPath.TryGetValue(assemblyName.Name!, out var path))
            {
                var asm = ctx.LoadFromAssemblyPath(path);
                return asm;
            }

            return default;
        }
        catch
        {
            return default;
        }
    }

    private void PopulateManagedLibsPath(string path, bool managedDll)
    {
        if (string.IsNullOrEmpty(path)) return;

        var dict = managedDll ? _managedLibsPath : _unManagedLibsPath;

        FileUtils.LoadDllPath(path, dict, managedDll);
    }

    internal bool CanInstall(AddinDescriptor addinDescriptor)
    {
        if (Options.DisableAddins != null && Options.DisableAddins.Any(p => Equals(p, addinDescriptor.Name))) return false;
        if (addinDescriptor.Enabled == false) return false;
        if (addinDescriptor.StartLevel > Options.MaxStartLevel) return false;
        return true;
    }

    internal void FireErrorOccurred(Exception ex)
    {
        this.FrameworkErrorOccurred?.Invoke(this, ex);
    }

    internal void FireFrameStateChanged(FrameworkState state)
    {
        State = state;
        FramewormStateChanged?.Invoke(this);
    }

    internal void FireAddinChanged(AddinActivator activator, AddinState state)
    {
        activator.Addin.State = state;
        if (_addinChanged != null)
        {
            foreach (var item in _addinChanged.GetInvocationList())
            {
                try
                {
                    item.DynamicInvoke(activator.Addin);
                }
                catch (Exception e)
                {
                    Log.Error($"{e.Message}",e);
                }
            }
        }
    }

    internal void RemoveAllAddinListeners(AddinActivator addin)
    {
        if (_addinChanged == null) return;

        var items = _addinChanged.GetInvocationList();
        var activator = (addin.InnerActivator is DelegateActivator activatorDelegate) ? activatorDelegate.Instance : addin.InnerActivator;
        items = items.Where(p => p.Target == activator).ToArray();

        foreach (var item in items)
            _addinChanged = Delegate.Remove(_addinChanged, item);
    }

    #region 公共方法

    private Delegate? _addinChanged;
    private bool disposedValue;

    public event Action<IGaf>? FramewormStateChanged;

    public event Action<IAddin>? AddinStateChanged
    {
        add
        {
            _addinChanged = Delegate.Combine(_addinChanged, value);
        }
        remove
        {
            if (value == null)
                _addinChanged = null;
            else if (_addinChanged != null)
                _addinChanged = Delegate.Remove(_addinChanged!, value);
        }
    }

    public event Action<IGaf,Exception>? FrameworkErrorOccurred;

    public GafOptions Options { get; }
    public IAddinManager AddinManager { get; }
    public IServiceCollection? Services { get; private set; }
    public IServiceProvider? Provider { get; private set; }

    internal IEnumerable<AddinActivator> Addins => _repository.addinActivatorsByInstallOrder;

    public FrameworkState State { get; internal set; }

    public void Configure(IServiceCollection? services, IConfiguration? configuration)
    {

        if (_systemAddinActivator != null) return;
        services?.AddSingleton<IGaf>(this);
        services?.AddSingleton<IAddinManager>(this.AddinManager);
        _systemAddinActivator = new SystemAddinActivator(this);
        _systemAddinActivator.Configure(services, configuration);
    }

    public void Start(IServiceProvider provider)
    {
        if (_systemAddinActivator == null) throw new InvalidOperationException("Addin " + ToString() + " cannot be started, since it is not Configured state.");
        Provider = provider;
        //启动插件
        _systemAddinActivator.Start(provider);
    }

    public void Shutdown()
    {
        if (_systemAddinActivator == null) return;

        List<Exception> errors = new();

        try
        {
            FireFrameStateChanged(FrameworkState.Uninstalling);
            _systemAddinActivator.Uninstall(Provider);
        }
        catch (Exception e)
        {
            FireErrorOccurred(e);
            errors.Add(e);
        }
        finally
        {
            FireFrameStateChanged(FrameworkState.Uninstalled);
        }

        foreach (AddinActivator activator in _repository.Activators)
        {
            if (Options.IsGpk && Options.AutoDeleteUncompressDirectory)
            {
                try
                {
                    if (Directory.Exists(activator.Addin.Location)) Directory.Delete(activator.Addin.Location, true);
                }
                catch (Exception e)
                {
                    errors.Add(e);
                }
            }

            activator.InnerActivator = null!;
        }

        _addinChanged = null;
        _repository.Clear();

        FramewormStateChanged = null;
        FrameworkErrorOccurred = null;

        foreach (var e in errors) Log.Error(e.Message,e);
    }

    private void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                Shutdown();

                _assemblyLoader.Resolving -= OnAssemblyResolving;
                _assemblyLoader.ResolvingUnmanagedDll -= OnResovingUnmanagedDll;
                _logListener?.Dispose();
                foreach(var ptr in _unmanagedLibs)
                {
                    NativeLibrary.Free(ptr);
                }
            }

            // TODO: 释放未托管的资源(未托管的对象)并重写终结器
            // TODO: 将大型字段设置为 null
            disposedValue = true;
        }
    }

    void IDisposable.Dispose()
    {
        // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    #endregion 公共方法
}
